#include "old_Statistics.h"
#include <boost/foreach.hpp>
#include <boost/tokenizer.hpp>
#include <set>
#include <limits>

typedef std::tr1::unordered_map<string, Relation*>::iterator Rel_itr;
typedef pair<string, Relation *> RelPair;
typedef std::tr1::unordered_map<string, int>::iterator Att_itr;
typedef pair<string, int> AttPair;
using namespace boost;

Relation::Relation() {
	isSingleton = true;
	numTuples = 0;
	JnumTuples = 0.0;
	numAttrs = 0.0;
}

Relation::~Relation() {
	attrs.clear();
	numAttrs = 0;
	numTuples = 0.0;
	JnumTuples = 0.0;
}

Statistics::Statistics() {
	numRels = 0;
}

Statistics::Statistics(Statistics &copyMe) {
	numRels = copyMe.numRels;
	Rel_itr itr1;

	for (itr1 = copyMe.relations.begin(); itr1 != copyMe.relations.end();
			itr1++) {
		Relation *o = itr1->second;
		Relation *n = new Relation();
		n->numTuples = o->numTuples;
		n->JnumTuples = o->JnumTuples;
		n->numAttrs = o->numAttrs;
		n->isSingleton = o->isSingleton;
		n->setName = o->setName;
		Att_itr itr2;
		for (itr2 = o->attrs.begin(); itr2 != o->attrs.end(); itr2++) {
			AttPair ap(itr2->first, itr2->second);
			n->attrs.insert(ap);
		}
		RelPair rp(itr1->first, n);
		relations.insert(rp);
	}
}

Statistics::~Statistics() {
	Rel_itr itr;
	for (itr = relations.begin(); itr != relations.end(); itr++) {
		delete itr->second;
	}
	numRels = 0;
}

void Statistics::AddRel(char *relName, int numTuples) {
	Rel_itr itr = relations.find(relName);

	if (itr != relations.end()) {
		Relation *r = itr->second;
		//if (r->isSingleton)
		r->numTuples = (double) numTuples;
		r->JnumTuples = (double) numTuples;
	} else {
		Relation *r = new Relation();
		r->numTuples = (double) numTuples;
		r->JnumTuples = (double) numTuples;
		r->numAttrs = 0;
		r->setName = relName;
		r->isSingleton = true;
		RelPair t(relName, r);
		relations.insert(t);
		numRels++;
	}
}
void Statistics::AddAtt(char *relName, char *attName, int numDistincts) {
	Rel_itr itr = relations.find(relName);

	if (itr != relations.end()) {
		Relation *r = itr->second;
		/*if (!r->isSingleton)
		 return;*/
		Att_itr itr1 = r->attrs.find(attName);
		if (itr1 != r->attrs.end()) {
			AttPair t1(attName, numDistincts);
			r->attrs.erase(attName);
			r->attrs.insert(t1);
		} else {
			AttPair t1(attName, numDistincts);
			r->attrs.insert(t1);
			r->numAttrs++;
		}
	} else {
		cout << "Error ! The relation " << relName << " not found!\n";
		return;
	}
}

void Statistics::CopyRel(char *oldName, char *newName) {
	Rel_itr itr = relations.find(oldName);

	if (itr == relations.end()) {
		cout << "ERROR. The original relation is not found!!!\n";
		return;
	}
	Relation *o = itr->second;
	/*if (!o->isSingleton)
	 return;*/
	Relation *n = new Relation();
	n->numTuples = o->numTuples;
	n->JnumTuples = o->JnumTuples;
	n->setName = o->setName;
	n->isSingleton = true;
	n->numAttrs = o->numAttrs;

	Att_itr itr1;
	for (itr1 = o->attrs.begin(); itr1 != o->attrs.end(); itr1++) {
		string dot = ".";
		string s = newName + dot + itr1->first;

		//AttPair ap (itr1->first, itr1->second);
		AttPair ap(s, itr1->second);
		n->attrs.insert(ap);
	}
	RelPair t(newName, n);
	relations.insert(t);
	numRels++;
}

void Statistics::Read(char *fromWhere) {
	char temp[1024];
	FILE *fp = fopen(fromWhere, "r");
	fscanf(fp, "%d", &numRels);

	for (int i = 0; i < numRels; i++) {
		string name;
		int isSingleton;
		Relation *p = new Relation();
		fscanf(fp, "%s", temp);
		name = temp;
		fscanf(fp, "%d", &isSingleton);
		p->isSingleton = isSingleton ? true : false;
		fscanf(fp, "%lf", &(p->numTuples));
		fscanf(fp, "%lf", &(p->JnumTuples));
		fscanf(fp, "%d", &(p->numAttrs));
		//p->isSingleton = true;
		//p->JnumTuples = p->numTuples;
		p->setName = name;
		for (int j = 0; j < p->numAttrs; j++) {
			string attName;
			int numDistincts;
			fscanf(fp, "%s", temp);
			attName = temp;
			fscanf(fp, "%d", &numDistincts);
			AttPair ap(attName, numDistincts);
			p->attrs.insert(ap);
		}
		RelPair rp(name, p);
		relations.insert(rp);
	}
	fclose(fp);

}
void Statistics::Write(char *fromWhere) {
	FILE *fp = fopen(fromWhere, "w");

	fprintf(fp, "%d ", numRels);

	Rel_itr itr1;

	for (itr1 = relations.begin(); itr1 != relations.end(); itr1++) {
		fprintf(fp, "%s ", ((string) (itr1->first)).c_str());
		Relation *p = itr1->second;
		int isSingleton = p->isSingleton ? 1 : 0;
		fprintf(fp, "%d ", isSingleton);
		fprintf(fp, "%lf ", p->numTuples);
		fprintf(fp, "%lf ", p->JnumTuples);
		fprintf(fp, "%d ", p->numAttrs);

		Att_itr itr2;
		for (itr2 = p->attrs.begin(); itr2 != p->attrs.end(); itr2++) {
			fprintf(fp, "%s %d ", ((string) (itr2->first)).c_str(),
					itr2->second);
		}
	}
	fclose(fp);
}


RelData Statistics::findAtt(string att, char *relNames[], int numToJoin) {
	RelData rval = { NULL, "", -1 };
	for (int i = 0; i < numToJoin; i++) {
		string r = relNames[i];
		Rel_itr itr = relations.find(r);
		if (itr == relations.end())
			continue;
		Relation *rl = itr->second;
		Att_itr itr1 = rl->attrs.find(att);
		if (itr1 != rl->attrs.end()) {
			rval.rel = rl;
			rval.name = r;
			rval.count = itr1->second;
			return rval;
		}
	}
	return rval;;
}

void Statistics::Apply(struct AndList *parseTree, char *relNames[],	int numToJoin) {

	if (validate(relNames, numToJoin)) {
		if (parseTree == NULL) {


				string newSetName;
				double newNumTuples = 1.0;
				int numSets = 0;

				if (parseTree == NULL) {
					set<string> processedRelSet;

					for (int i = 0; i < numToJoin; i++) {
						Rel_itr itr = relations.find(relNames[i]);
						Relation* rel = itr->second;
						if (rel->isSingleton) {
							newNumTuples *= rel->numTuples;
							numSets++;
						} else {
							set<string>::iterator it = processedRelSet.find(rel->setName);
							if (it != processedRelSet.end()) {
								continue;
							} else {
								string setName = rel->setName;

								set<string> strs;
								//boost::split( strs, setName, boost::is_any_of(" "));

								char_separator<char> sep(" ");
								tokenizer < char_separator<char> > tokens(setName, sep);
								BOOST_FOREACH(string s, tokens)
								{
									processedRelSet.insert(s);
								}

								newNumTuples *= rel->JnumTuples;
								numSets++;
							}
						}
					}
				if (numSets != 1)
				{
					for (int i = 0; i < numToJoin; i++) {
						if (i != numToJoin - 1) {
							newSetName.append(relNames[i]).append(" ");
						} else {
							newSetName.append(relNames[i]);
						}
					}
					for (int i = 0; i < numToJoin; i++) {
						Rel_itr itr = relations.find(relNames[i]);
						Relation* rel = itr->second;
						rel->isSingleton = false;
						rel->JnumTuples = newNumTuples;
						rel->setName = newSetName;
					}
				}
			}
		} else {
			struct AndList *pAnd = parseTree;
			while (pAnd != NULL) {
				bool independant = false;
				bool join = false;
				double factor;
				struct OrList *pOr = pAnd->left;
				set<string> temp;
				Relation * R;
				while (pOr != NULL) {
					struct OrList *pOr1 = pOr;
					while (pOr1 != NULL) {
						struct ComparisonOp *pCom1 = pOr1->left;
						if (pCom1 != NULL) {
							struct Operand *pOperand = NULL;
							pOperand = pCom1->left;
							string attLeft = pOperand->value;
							RelData lRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *l = lRel.rel;
							pOperand = pCom1->right;
							string attRight = pOperand->value;
							RelData rRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *r = rRel.rel;
							if ((l == NULL && r == NULL)
									|| (l != NULL && r != NULL)) {
								if ((l != NULL && r != NULL)
										&& (lRel.name != rRel.name))
									join = true;
								break;
							}
							if (l != NULL) {
								R = l;
								pair<set<string>::iterator, bool> status;
								status = temp.insert(attLeft);
								if (status.second == true && temp.size() > 1)
									break;
							}
							if (r != NULL) {
								R = r;
								pair<set<string>::iterator, bool> status;
								status = temp.insert(attRight);
								if (status.second == true && temp.size() > 1)
									break;
							}
						}
						pOr1 = pOr1->rightOr;
					}
					if (pOr1 != NULL)
						independant = true;
					if (independant)
						factor = 1.0;
					else
						factor = 0.0;
					while (pOr != NULL) {
						struct ComparisonOp *pCom = pOr->left;
						if (pCom != NULL) {
							struct Operand *pOperand = NULL;
							pOperand = pCom->left;
							RelData lRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *l = lRel.rel;
							pOperand = pCom->right;
							RelData rRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *r = rRel.rel;
							/*pOperand = pCom->left;
							 Relation *l = findAtt(pOperand->value, relNames, numToJoin);
							 pOperand = pCom->right;
							 Relation *r = findAtt(pOperand->value, relNames, numToJoin);
							 */
							if (l == NULL && r == NULL) {
								cerr
										<< "Found an experssion clause in the AndTree which contains the Operands "
										<< pCom->left->value << " & "
										<< pCom->right->value
										<< " which are not found in the relations given !!!\n";
								return;
							}

							switch (pCom->code) {
							case 5:
							case 6:
								if ((l == NULL && r != NULL)
										|| (l != NULL && r == NULL)) {
									if (independant)
										factor *= 2.0 / 3.0;
									else
										factor += 1.0 / 3.0;
								}
								break;
							case 7:
								if ((l == NULL && r != NULL)
										|| (l != NULL && r == NULL)) {
									int count;
									double numTuples;
									if (l != NULL) {
										count = lRel.count;
										if (l->isSingleton)
											numTuples = l->numTuples;
										else
											numTuples = l->JnumTuples;
									} else {
										count = rRel.count;
										if (r->isSingleton)
											numTuples = r->numTuples;
										else
											numTuples = r->JnumTuples;
									}
									if (count == -1) {
										if (independant)
											factor *= (1.0 - (1.0 / numTuples));
										else
											factor += (1.0 / numTuples);
									} else {
										if (independant)
											factor *= (1.0 - (1.0 / count));
										else
											factor += (1.0 / count);
									}
								}
								if (l != NULL && r != NULL) {
									set<string> joins;
									string setName = "";
									double JnumTuples, LnumTuples, RnumTuples;
									int lcount, rcount;
									lcount = lRel.count;
									rcount = rRel.count;
									if (lcount == -1) {
										if (l->isSingleton)
											lcount = l->numTuples;
										else
											lcount = l->JnumTuples;
									}
									if (rcount == -1) {
										if (r->isSingleton)
											rcount = r->numTuples;
										else
											rcount = r->JnumTuples;
									}

									if (l->isSingleton) {
										setName += lRel.name;
										joins.insert(lRel.name);
										LnumTuples = l->numTuples;
									} else {
										LnumTuples = l->JnumTuples;
										setName += l->setName;
										char_separator<char> sep(" ");
										tokenizer < char_separator<char> > tokens(l->setName, sep);
									BOOST_FOREACH (string t, tokens)
									{
										joins.insert(t);
									}
								}
									if (r->isSingleton) {
										setName = setName + " " + rRel.name;
										joins.insert(rRel.name);
										RnumTuples = r->numTuples;
									} else {
										RnumTuples = r->JnumTuples;
										setName = setName + " " + r->setName;
										char_separator<char> sep(" ");
										tokenizer < char_separator<char> > tokens(r->setName, sep);
									BOOST_FOREACH (string t, tokens)
									{
										joins.insert(t);
									}
								}

									int max = lcount > rcount ? lcount : rcount;
									JnumTuples = (LnumTuples * RnumTuples)
											/ max;
									set<string>::iterator it;
									for (it = joins.begin(); it != joins.end();
											it++) {
										Rel_itr itr = relations.find(*it);
										Relation *r1 = itr->second;
										r1->isSingleton = false;
										r1->JnumTuples = JnumTuples;
										r1->setName = setName;
									}
								}
								break;
							default:
								cerr << "Invalid Operator code " << pCom->code;
								return;
							}

						}
						pOr = pOr->rightOr;
					}
					if (!join) {
						if (independant)
							factor = 1.0 - factor;

						if (R->isSingleton)
							R->numTuples *= factor;
						else {
							char_separator<char> sep(" ");
							tokenizer < char_separator<char> > tokens(R->setName, sep);
						BOOST_FOREACH (string t, tokens)
						{
							Rel_itr itr = relations.find(t);
							Relation *rl = itr->second;
							rl->JnumTuples *= factor;
						}
					}
				}
			}
				pAnd = pAnd->rightAnd;
			}
		}
	}
}

double Statistics::Estimate(struct AndList *parseTree, char **relNames,	int numToJoin) {

	double answer = numeric_limits<double>::max();
	if (validate(relNames, numToJoin)) {
		if (parseTree == NULL) {


			string newSetName;
			//	double newNumTuples = 1.0;
				answer = 1.0;
					set<string> processedRelSet;

					for (int i = 0; i < numToJoin; i++) {
						Rel_itr itr = relations.find(relNames[i]);
						Relation* rel = itr->second;
						if (rel->isSingleton) {
							answer *= rel->numTuples;

						} else {
							set<string>::iterator it = processedRelSet.find(rel->setName);
							if (it != processedRelSet.end()) {
								continue;
							} else {
								string setName = rel->setName;
								set<string> strs;
								//boost::split( strs, setName, boost::is_any_of(" "));

								char_separator<char> sep(" ");
								tokenizer < char_separator<char> > tokens(setName, sep);
								BOOST_FOREACH(string s, tokens)
								{
									processedRelSet.insert(s);
								}

								answer *= rel->JnumTuples;

							}

						}
					}
			return answer;


		} else {

			for (int i = 0; i < numToJoin; i++) {
				Rel_itr itr = relations.find(relNames[i]);
				Relation *rl = itr->second;
				rl->JsetName = rl->setName;
				if (rl->isSingleton)
					rl->EnumTuples = rl->numTuples;
				else
					rl->EnumTuples = rl->JnumTuples;
			}

			struct AndList *pAnd = parseTree;
			while (pAnd != NULL) {
				bool independant = false;
				bool join = false;
				double factor;
				struct OrList *pOr = pAnd->left;
				set<string> temp;
				Relation * R;
				while (pOr != NULL) {
					struct OrList *pOr1 = pOr;
					while (pOr1 != NULL) {
						struct ComparisonOp *pCom1 = pOr1->left;
						if (pCom1 != NULL) {
							struct Operand *pOperand = NULL;
							pOperand = pCom1->left;
							string attLeft = pOperand->value;
							RelData lRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *l = lRel.rel;
							pOperand = pCom1->right;
							string attRight = pOperand->value;
							RelData rRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *r = rRel.rel;
							if ((l == NULL && r == NULL)
									|| (l != NULL && r != NULL)) {
								if ((l != NULL && r != NULL)
										&& (lRel.name != rRel.name))
									join = true;
								break;
							}
							if (l != NULL) {
								R = l;
								pair<set<string>::iterator, bool> status;
								status = temp.insert(attLeft);
								if (status.second == true && temp.size() > 1)
									break;
							}
							if (r != NULL) {
								R = r;
								pair<set<string>::iterator, bool> status;
								status = temp.insert(attRight);
								if (status.second == true && temp.size() > 1)
									break;
							}
						}
						pOr1 = pOr1->rightOr;
					}
					if (pOr1 != NULL)
						independant = true;
					if (independant)
						factor = 1.0;
					else
						factor = 0.0;
					while (pOr != NULL) {
						struct ComparisonOp *pCom = pOr->left;
						if (pCom != NULL) {
							struct Operand *pOperand = NULL;
							pOperand = pCom->left;
							RelData lRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *l = lRel.rel;
							pOperand = pCom->right;
							RelData rRel = findAtt(pOperand->value, relNames,
									numToJoin);
							Relation *r = rRel.rel;
							/*pOperand = pCom->left;
							 Relation *l = findAtt(pOperand->value, relNames, numToJoin);
							 pOperand = pCom->right;
							 Relation *r = findAtt(pOperand->value, relNames, numToJoin);
							 */
							if (l == NULL && r == NULL) {
								cerr
										<< "Found an experssion clause in the AndTree which contains the Operands "
										<< pCom->left->value << " & "
										<< pCom->right->value
										<< " which are not found in the relations given !!!\n";
							}

							switch (pCom->code) {
							case 5:
							case 6:
								if ((l == NULL && r != NULL)
										|| (l != NULL && r == NULL)) {
									if (independant)
										factor *= 2.0 / 3.0;
									else
										factor += 1.0 / 3.0;
								}
								break;
							case 7:
								if ((l == NULL && r != NULL)
										|| (l != NULL && r == NULL)) {
									int count;
									double numTuples;
									if (l != NULL) {
										count = lRel.count;
										numTuples = l->EnumTuples;
									} else {
										count = rRel.count;
										numTuples = r->EnumTuples;
									}
									if (count == -1) {
										if (independant)
											factor *= (1.0 - (1.0 / numTuples));
										else
											factor += (1.0 / numTuples);
									} else {
										if (independant)
											factor *= (1.0 - (1.0 / count));
										else
											factor += (1.0 / count);
									}
								}
								if (l != NULL && r != NULL) {
									set<string> joins;
									string setName = "";
									double EnumTuples, LnumTuples, RnumTuples;
									int lcount, rcount;
									lcount = lRel.count;
									rcount = rRel.count;
									if (lcount == -1) {
										lcount = l->EnumTuples;
									}
									if (rcount == -1) {
										rcount = r->EnumTuples;
									}

									if (l->isSingleton) {
										setName += lRel.name;
										joins.insert(lRel.name);
										LnumTuples = l->EnumTuples;
									} else {
										setName += l->setName;
										LnumTuples = l->EnumTuples;
										char_separator<char> sep(" ");
										tokenizer < char_separator<char> > tokens(l->setName, sep);
									BOOST_FOREACH (string t, tokens)
									{
										joins.insert(t);
									}
								}
									if (r->isSingleton) {
										setName = setName + " " + rRel.name;
										joins.insert(rRel.name);
										RnumTuples = r->EnumTuples;
									} else {
										setName = setName + " " + r->setName;
										RnumTuples = r->EnumTuples;
										char_separator<char> sep(" ");
										tokenizer < char_separator<char> > tokens(r->setName, sep);
									BOOST_FOREACH (string t, tokens)
									{
										joins.insert(t);
									}
								}

									int max = lcount > rcount ? lcount : rcount;
									EnumTuples = (LnumTuples * RnumTuples)
											/ max;
									set<string>::iterator it;
									for (it = joins.begin(); it != joins.end();
											it++) {
										Rel_itr itr = relations.find(*it);
										Relation *r1 = itr->second;
										r1->EnumTuples = EnumTuples;
										r1->JsetName = setName;
									}
									answer = EnumTuples;
								}
								break;
							default:
								cerr << "Invalid Operator code " << pCom->code;
								return 0.0;
							}

						}
						pOr = pOr->rightOr;
					}
					if (!join) {
						if (independant)
							factor = 1.0 - factor;

						if (R->isSingleton)
							R->EnumTuples *= factor;
						else {
							char_separator<char> sep(" ");
							tokenizer < char_separator<char> > tokens(R->JsetName, sep);
						BOOST_FOREACH (string t, tokens)
						{
							Rel_itr itr = relations.find(t);
							Relation *rl = itr->second;
							rl->EnumTuples *= factor;
						}
					}
						answer = R->EnumTuples;
					}
				}
				pAnd = pAnd->rightAnd;
			}
		}
	}
	return answer;
}

bool Statistics::validate(char ** relNames, int numToJoin) {
	set<string> relSet;
	set<string> newRelSet;
	for (int i = 0; i < numToJoin; i++) {
		relSet.insert(relNames[i]);
	}

	for (int i = 0; i < numToJoin; i++) {
		set<string>::iterator it = newRelSet.find(relNames[i]);
		if (it != newRelSet.end()) {
			continue;
		}
		Rel_itr itr = relations.find(relNames[i]);
		Relation* rel = itr->second;
		if (!rel->isSingleton) {
			string setName = rel->setName;
			set<string> strs;
			//boost::split( strs, setName, boost::is_any_of(" "));

			char_separator<char> sep(" ");
			tokenizer < char_separator<char> > tokens(setName, sep);
			BOOST_FOREACH(string s, tokens)
			{
				set<string>::iterator its = relSet.find(s);
				if (its != relSet.end()) {
					newRelSet.insert(*its);
				} else {
					cout << "Invalid Relation Sets for Join";
					exit(1);
				}
			}
		}
	}
	return true;
}
